//
//  File.swift
//  
//
//  Created by ellzu gu on 2021/9/3.
//

import Foundation

public class DataPacket: InputPacket, OutputPacket {
    
    public let header: PacketHeader
    public private(set) var data: Data
    
    public init(_ header: PacketHeader, data: Data = Data()) {
        self.header = header
        self.data = data
    }
    
//MARK: READ
    public var inputHeader: PacketHeader { self.header }
    public private(set) var dataOffset: UInt64 = 0
    public func readData(_ buf: UnsafeMutableRawPointer, bufCount: UInt64) -> Int {
        let copyLen = min(self.data.count - Int(self.dataOffset), Int(bufCount))
        guard copyLen > 0 else {
            return 0
        }
        let data = self.data.advanced(by: Int(self.dataOffset))
        data.copyBytes(to: buf.assumingMemoryBound(to: UInt8.self), count: copyLen)
        self.dataOffset += UInt64(copyLen)
        return copyLen
    }
    
//MARK: WRITE
    public var outputHeader: PacketHeader? { self.header }
    public func writeData(_ buf: UnsafeRawPointer, count: UInt64) {
        self.data.append(buf.assumingMemoryBound(to: UInt8.self), count: Int(count))
    }
    public var writeCount: UInt64 { UInt64(self.data.count) }
}

extension DataPacket {
    public static func packet(_ map: [String : Any]) -> DataPacket? {
        var data: Data? = nil
        do {
            data = try JSONSerialization.data(withJSONObject: map, options: [.prettyPrinted])
        } catch {

        }
        guard let data = data else {
            return nil
        }
        let header = PacketHeader.header(0x01)!
        header.type = .Map
        header.dataCount = UInt64(data.count)
//        header.identifier
        return DataPacket(header, data: data)
    }
    
    public func toMap() -> [String : Any]? {
        guard self.header.type == .Map else {
            return nil
        }
        let map: [String : Any]?
        do {
            map = try JSONSerialization.jsonObject(with: self.data, options: .mutableContainers) as? [String : Any] ?? nil
        } catch {
            map = nil
        }
        return map
    }
    
}
